package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Repository;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.bean.BeanDefination;
import com.lagou.edu.bean.FieldDefination;
import com.lagou.edu.bean.Propagation;
import com.lagou.edu.bean.TransactionInfo;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @ClassName DefaultBeanFactory
 * @Description
 * @Author playboy
 * @Date 2021/4/20 2:14 下午
 * @Version 1.0
 **/
public class DefaultBeanFactory implements BeanFactory {

    List<BeanDefination> beanDefinations = new ArrayList<>();

    Map<Class<?>, BeanDefination> beanDefinationMap = new HashMap<>();

    Map<String, Object> beans = new HashMap<>();

    Map<Class<?>, Object> beanWithClass = new HashMap<>();

    String rootPath = "";

    List<Class<?>> classes = new ArrayList<>();

    /**
     * 通过配置文件创建BeanFactory
     *
     * @param path
     * @throws Exception
     */
    public DefaultBeanFactory(String path) throws Exception {
        //1.解析xml
        InputStream in = DefaultBeanFactory.class.getClassLoader().getResourceAsStream(path);
        Document read = new SAXReader().read(in);
        Element rootElement = read.getRootElement();

        List<Element> beans = rootElement.selectNodes("//bean");

        //解析bean
        for (Element bean : beans) {
            String id = bean.attributeValue("id");
            String classStr = bean.attributeValue("class");
            Class<?> aClass = Class.forName(classStr);
            BeanDefination beanDefination = doBeanDefination(id, aClass);
            List<Element> properties = bean.elements("property");
            for (Element property : properties) {
                beanDefination.getProperties().put(property.attributeValue("name"), property.attributeValue("value"));
            }
        }

        //2.判断是否需要扫描
        List<Element> scans = rootElement.selectNodes("//component-scan");
        if (scans != null && scans.size() > 0) {
            Element scan = scans.get(0);
            String packageName = scan.attributeValue("base-package");
            scan(packageName);
        }

        //3.生成bean
        createBean();
    }


    /**
     * 通过指定类路径来创建BeanFactory
     *
     * @param clazz
     */
    public DefaultBeanFactory(Class<?> clazz) throws Exception {

        //1.扫描逻辑
        scan(clazz.getPackageName());

        //2.生成bean
        createBean();

    }

    /**
     * 扫描逻辑
     *
     * @param packageName
     */
    private void scan(String packageName) throws IOException {
        //1.根据扫描的包路径，获取扫描的包下的所有class文件
        parsePackage(packageName);

        //2.解析每一个class生成BeanDefination
        createBeanDefination();
    }

    /**
     * 处理beanDefinations生成每一个bean,暂未解决循环依赖的问题
     */
    private void createBean() throws Exception {
        for (BeanDefination beanDefination : beanDefinations) {
            doCreateBean(beanDefination.getClassType());
        }
    }

    /**
     * 根据beanDefination生成一个对象
     *
     * @param aClass
     * @return
     * @throws Exception
     */
    private Object doCreateBean(Class aClass) throws Exception {
        //先判断是否已经生成了
        Object obj = getObject(aClass);
        if (obj != null) {
            return obj;
        }
        //如果创进来的是接口，需要找到当前这个接口的实现类
        BeanDefination beanDefination = null;
        if (aClass.isInterface()) {
            //判断接口的子类
            for (BeanDefination defination : beanDefinations) {
                Class<?>[] interfaces = defination.getClassType().getInterfaces();
                if (interfaces.equals(aClass)) {
                    beanDefination = defination;
                    break;
                }
            }
        } else {
            beanDefination = beanDefinationMap.get(aClass);
        }
        //如果没有找到对应的BeanDefination，抛出异常
        if (beanDefination == null) {
            throw new RuntimeException(aClass.getName() + "没有找到对应的类型");
        }
        //1.通过反射生成Bean
        Class<?> classType = beanDefination.getClassType();
        Object o = classType.newInstance();

        //2.填充Bean
        //2.1填充依赖
        List<FieldDefination> fieldDefinations = beanDefination.getFieldDefinations();
        for (FieldDefination fieldDefination : fieldDefinations) {
            String fieldName = fieldDefination.getFieldName();
            Object object = getObject(fieldDefination.getaClass());
            //判断依赖的对象是否已经生成，如果没有生成的话，调用生成bean
            if (object == null) {
                object = doCreateBean(fieldDefination.getaClass());
            }
            Field declaredField = classType.getDeclaredField(fieldName);
            declaredField.setAccessible(true);
            declaredField.set(o, object);
        }
        //2.2填充属性
        Properties properties = beanDefination.getProperties();
        Set<Map.Entry<Object, Object>> entries = properties.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            String property = entry.getKey().toString();
            property = property.substring(0, 1).toUpperCase() + property.substring(1);
            Method method = classType.getMethod("set" + property, String.class);
            method.invoke(o, entry.getValue());
        }

        //3.查看是否生成代理类
        TransactionInfo transactionInfo = beanDefination.getTransactionInfo();
        if (transactionInfo != null) {
            ProxyFactory proxyFactory = getObject(ProxyFactory.class);
            if (proxyFactory == null) {
                proxyFactory = (ProxyFactory) doCreateBean(ProxyFactory.class);
            }
            Class<?>[] interfaces = classType.getInterfaces();
            if (interfaces != null && interfaces.length > 0) {
                o = proxyFactory.getJdkProxy(o); //有接口，动态代理
            } else {
                o = proxyFactory.getCglibProxy(o);//没有接口，cglib代理
            }
        }


        beans.put(beanDefination.getId(), o);
        return o;
    }


    /**
     * 处理每一个class，并解析每一个class上的注解
     */
    private void createBeanDefination() {
        for (Class<?> aClass : classes) {
            Service serviceAnnotation = aClass.getAnnotation(Service.class);
            Repository repositoryAnnotation = aClass.getAnnotation(Repository.class);
            if (serviceAnnotation != null || repositoryAnnotation != null) {
                //解析id
                String value = null;
                if (serviceAnnotation != null) {
                    value = serviceAnnotation.value();
                } else {
                    value = repositoryAnnotation.value();
                }
                if (!(value != null && !value.equals(""))) {
                    String simpleName = aClass.getSimpleName();
                    simpleName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
                    value = simpleName;
                }
                doBeanDefination(value, aClass);
            }
        }
    }

    /**
     * 解析class生成BeanDefination
     *
     * @param id
     * @param aClass
     * @return
     */
    public BeanDefination doBeanDefination(String id, Class<?> aClass) {
        BeanDefination beanDefination = new BeanDefination();
        beanDefination.setId(id);
        beanDefination.setClassType(aClass);
        //解析依赖
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            Autowired autowired = field.getAnnotation(Autowired.class);
            if (autowired != null) {
                FieldDefination fieldDefination = new FieldDefination();
                fieldDefination.setFieldName(field.getName());
                fieldDefination.setaClass(field.getType());
                beanDefination.getFieldDefinations().add(fieldDefination);
            }
        }
        //解析事务配置
        Transactional transactional = aClass.getAnnotation(Transactional.class);
        if (transactional != null) {
            Propagation propagation = transactional.propagation();
            TransactionInfo transactionInfo = new TransactionInfo();
            transactionInfo.setPropagation(propagation);
            beanDefination.setTransactionInfo(transactionInfo);
        }
        beanDefinations.add(beanDefination);
        beanDefinationMap.put(beanDefination.getClassType(), beanDefination);
        return beanDefination;
    }

    /**
     * 解析包路径，获取扫描到的所有class文件
     *
     * @param packageName
     * @throws IOException
     */
    private void parsePackage(String packageName) throws IOException {
        packageName = packageName.replace(".", File.separator);
        URL resource = DefaultBeanFactory.class.getClassLoader().getResource(packageName);
        rootPath = resource.getPath().replace(packageName, "");
        Enumeration<URL> resources = DefaultBeanFactory.class.getClassLoader().getResources(packageName);
        while (resources.hasMoreElements()) {
            URL url = resources.nextElement();
            String file = url.getFile();
            File rootDir = new File(file);
            if (rootDir.isDirectory()) {
                //包
                parseDir(rootDir);
            }
        }
    }


    /**
     * 获取dir下所有的.class文件
     *
     * @param dir
     */
    private void parseDir(File dir) {
        File[] subFiles = dir.listFiles();
        for (File subFile : subFiles) {
            if (subFile.isDirectory()) {
                parseDir(subFile);
            } else {
                if (subFile.getName().endsWith(".class")) {
                    String className = subFile.getPath().replace(rootPath, "").replace(".class", "");
                    className = className.replace(File.separator, ".");
                    try {
                        Class<?> aClass = Class.forName(className);
                        classes.add(aClass);
                    } catch (Exception e) {
                        System.out.println(className + "加载失败");
                    }
                }
            }
        }
    }


    @Override
    public Object getObject(String beanName) {
        return beans.get(beanName);
    }

    @Override
    public <T> T getObject(Class<T> aClass) {
        Object o = beanWithClass.get(aClass);
        if (o == null) {
            //没有获取到，就要取添加
            for (Map.Entry<String, Object> entry : beans.entrySet()) {
                if (entry.getValue().getClass().equals(aClass)) {
                    //如果匹配，就将这个加入到beanWithClass中返回
                    o = entry.getValue();
                    beanWithClass.put(aClass, o);
                    break;
                } else {
                    //如果不匹配,匹配每一个类的父类和接口
                    Class<?>[] interfaces = entry.getValue().getClass().getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        if (anInterface.equals(aClass)) {
                            o = entry.getValue();
                            beanWithClass.put(anInterface, o);
                            return (T) o;
                        }
                    }
                }
            }
        }
        return (T) o;
    }
}
